﻿using Cysharp.Threading.Tasks;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.EventSystems;

public class UIManager : GameFrameClassBase
{
    #region 缓存静态变量

    public static Canvas canvas, worldCanvas;

    private static Dictionary<string, UIPanelBase> s_CachedPanels = new Dictionary<string, UIPanelBase>();
    private static List<UIPanelBase> s_ShowingPanels = new List<UIPanelBase>();
    private static List<string> s_LoadingPanels = new List<string>();
    private static List<Transform> s_Roots = new List<Transform>();

    #endregion 缓存静态变量

    #region 方法重载

    public override List<Type> dependences => new List<Type> { typeof(InputManager) };

    public override async UniTask InitAsync()
    {
        //canvas = GameObject.FindObjectsOfTypeAll<Canvas>();
        canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
        worldCanvas = GameObject.Find("WorldCanvas").GetComponent<Canvas>();

        GameObject.DontDestroyOnLoad(canvas);
        GameObject.DontDestroyOnLoad(worldCanvas);

        var types = Enum.GetValues(typeof(UIPanelType));
        foreach (UIPanelType type in types)
        {
            s_Roots.Add(CreateRoot(type.ToString()));
        }

        var eventSystem = GameObject.FindFirstObjectByType<EventSystem>();
        GameObject.DontDestroyOnLoad(eventSystem);

        var preloadUIs = Tools.GetSubClassesOf<UIPanelBase>();
        foreach (var type in preloadUIs)
        {
            await LoadAsync(type.Name);
        }
    }

    #endregion 方法重载

    #region Load方法

    /// <summary>
    /// 异步加载界面
    /// </summary>
    private static async UniTask<UIPanelBase> LoadAsync(string uiName)
    {
        var uiBase = Find(uiName);
        // 已缓存，则直接取出（保护用，一般不进入此状态）
        if (uiBase != null)
        {
            Debug.Log($"UIManager 界面资源加载失败：{uiName} 资源已实例化请勿重复加载");
            return uiBase;
        }

        // 正在加载，则等待其加载完毕，再直接从缓存中读取（保护用，一般不进入此状态）
        if (s_LoadingPanels.Contains(uiName))
        {
            Debug.LogWarning($"UIManager 界面资源加载失败：{uiName} 资源正在加载请勿重复操作");
            await UniTask.WaitUntil(() => !s_LoadingPanels.Contains(uiName));
            return s_CachedPanels[uiName];
        }

        // 未缓存，则加入缓存列表，等待缓存结束后取出
        s_LoadingPanels.Add(uiName);
        var go = await Addressables.InstantiateAsync($"UIs/{uiName}.prefab").Task;
        if (go == null)
        {
            Debug.LogError($"UIManager 界面资源加载失败：{uiName} 资源不存在");
            s_LoadingPanels.Remove(uiName);
            return null;
        }

        uiBase = go.GetComponent<UIPanelBase>();
        if (uiBase == null)
        {
            Addressables.Release(go);
            Debug.LogError($"UIManager 界面资源加载失败：{uiName} 预制体中未找到对应脚本");
            s_LoadingPanels.Remove(uiName);
            return null;
        }

        var root = s_Roots[(int)uiBase.type];
        go.name = uiName;
        go.transform.SetParent(root);
        go.SetActive(false);
        s_CachedPanels[uiName] = uiBase;
        s_LoadingPanels.Remove(uiName);
        Debug.Log($"UIManager 界面加载成功：{uiName}");
        uiBase.OnInit();
        FocusOn(uiBase);
        return uiBase;
    }

    #endregion Load方法

    #region Show方法

    /// <summary>
    /// 显示界面
    /// </summary>
    public static T Show<T>() where T : UIPanelBase
    {
        return Show(typeof(T).Name) as T;
    }

    /// <summary>
    /// 显示界面
    /// </summary>
    public static UIPanelBase Show(string uiName)
    {
        var uiBase = Find(uiName);
        if (uiBase == null)
        {
            Debug.LogError($"UIManager 界面显示失败：{uiName} 不存在");
            return null;
        }

        if (s_ShowingPanels.Contains(uiBase)) return uiBase;

        uiBase.gameObject.SetActive(true);
        uiBase.transform.SetAsLastSibling();
        SetUIFullScreen(uiBase.rectTransform);
        s_ShowingPanels.Add(uiBase);
        uiBase.OnShow();
        PlayShowingTween(uiBase).Forget();

        Debug.Log($"UIManager 界面显示成功：{uiName}");
        return uiBase;
    }

    #endregion Show方法

    #region Hide方法

    /// <summary>
    /// 隐藏界面
    /// </summary>
    public static void Hide<T>() where T : UIPanelBase
    {
        Hide(typeof(T).Name);
    }

    /// <summary>
    /// 隐藏界面
    /// </summary>
    public static async void Hide(string uiName)
    {
        var uiBase = FindShowing(uiName);
        if (uiBase == null) return;

        await PlayHidingTween(uiBase);
        uiBase.OnHide();
        uiBase.gameObject.SetActive(false);
        s_ShowingPanels.Remove(uiBase);

        Debug.Log($"UIManager 界面隐藏成功：{uiName}");
    }

    #endregion Hide方法

    #region Find方法

    /// <summary>
    /// 查找界面
    /// </summary>
    public static T Find<T>() where T : UIPanelBase
    {
        return Find(typeof(T).Name) as T;
    }

    /// <summary>
    /// 在界面缓存中查找界面
    /// </summary>
    public static UIPanelBase Find(string uiName)
    {
        s_CachedPanels.TryGetValue(uiName, out var uiBase);
        return uiBase;
    }

    /// <summary>
    /// 查找显示中的界面
    /// </summary>
    public static T FindShowing<T>() where T : UIPanelBase
    {
        return FindShowing(typeof(T).Name) as T;
    }

    /// <summary>
    /// 查找显示中的界面
    /// </summary>
    private static UIPanelBase FindShowing(string uiName)
    {
        UIPanelBase uiBase = s_ShowingPanels.Find(ui => ui.uiName == uiName);
        return uiBase;
    }

    #endregion Find方法

    #region Focus方法

    private static void FocusOn(UIPanelBase uiBase)
    {
        if (!s_ShowingPanels.Contains(uiBase)) return;
        uiBase.transform.SetAsLastSibling();
        Debug.Log($"UIManager 界面焦点设置成功：{uiBase.uiName}");
        uiBase.OnFocus();
    }

    //private static void FocusOn(string uiName)
    //{
    //}

    //private static void FocusOn<T>() where T : UIBase
    //{
    //}

    #endregion Focus方法

    #region Pause方法

    private static void Pause(string uiName)
    {
    }

    //private static void Pause(string uiName)
    //{
    //}

    //private static void Pause<T>() where T : UIBase
    //{
    //}

    #endregion Pause方法

    #region Resume方法

    private static void Resume方法(string uiName)
    {
    }

    //private static void Resume方法(string uiName)
    //{
    //}

    //private static void Resume方法<T>() where T : UIBase
    //{
    //}

    #endregion Resume方法

    #region 其他外部综合方法

    /// <summary>
    /// 切换界面，Show方法 和 Hide方法 的封装
    /// </summary>
    public static void Switch<T>() where T : UIPanelBase
    {
        UIPanelBase uiBase = FindShowing<T>();
        if (uiBase == null)
        {
            Show<T>();
        }
        else
        {
            Hide<T>();
        }
    }

    /// <summary>
    /// 切换界面，Show方法 和 Hide方法 的封装
    /// </summary>
    public static void Switch(string uiName)
    {
        UIPanelBase uiBase = FindShowing(uiName);
        if (uiBase == null)
        {
            Show(uiName);
        }
        else
        {
            Hide(uiName);
        }
    }

    ///// <summary>
    ///// 获取显示中的某类型界面数量
    ///// </summary>
    //public static int GetShowingPanelCount(UIPanelType panelType)
    //{
    //    return s_ShowingPanels.FindAll(p => p.type == panelType).Count;
    //}

    ///// <summary>
    ///// 获取显示中的某类型界面数量
    ///// </summary>
    //public static int GetShowingPanelCount(UIPanelType panelType1, UIPanelType panelType2)
    //{
    //    return s_ShowingPanels.FindAll(p => p.type == panelType1 || p.type == panelType2).Count;
    //}

    #endregion 其他外部综合方法

    #region 类内方法，不写在Utility中以降低耦合

    private Transform CreateRoot(string rootName)
    {
        GameObject go = new GameObject(rootName);
        RectTransform rect = go.AddComponent<RectTransform>();
        rect.SetParent(canvas.transform);
        rect.SetAsLastSibling();
        SetUIFullScreen(rect);
        return rect;
    }

    private static void SetUIFullScreen(RectTransform rect)
    {
        rect.localPosition = Vector3.zero;
        rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, canvas.pixelRect.width);
        rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, canvas.pixelRect.height);
        rect.anchorMin = Vector2.zero;
        rect.anchorMax = Vector2.one;
        rect.offsetMin = Vector2.zero;
        rect.offsetMax = Vector2.zero;
    }

    private static async UniTask PlayShowingTween(UIPanelBase uiBase)
    {
        await uiBase.OnShowingStart();
        switch (uiBase.showAnimaMode)
        {
            case UIShowAnimaMode.Fade:
                await uiBase.animaWindow.FadeShowAsync(uiBase.animaDuration);
                break;

            case UIShowAnimaMode.Popup:
                await uiBase.animaWindow.PopupShowAsync(uiBase.animaDuration);
                break;
        }
        await uiBase.OnShowingEnd();
    }

    private static async UniTask PlayHidingTween(UIPanelBase uiBase)
    {
        await uiBase.OnHidingStart();
        switch (uiBase.showAnimaMode)
        {
            case UIShowAnimaMode.Fade:
                await uiBase.animaWindow.FadeHideAsync(uiBase.animaDuration);
                break;

            case UIShowAnimaMode.Popup:
                await uiBase.animaWindow.PopupHideAsync(uiBase.animaDuration);
                break;
        }
        await uiBase.OnHidingEnd();
    }

    #endregion 类内方法
}